use crate::utils::db_utils::bson_to_json;
use chrono::{DateTime as CDateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use wither::{
    bson::{oid::ObjectId, Bson, DateTime},
    Model,
};

use super::{
    club::{SimpleClub, SimpleClubModel},
    user::{SimpleUser, SimpleUserModel},
    Assets,
};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum MessageReceiverType {
    #[serde(rename = "system")]
    System,
    #[serde(rename = "club")]
    Club,
    #[serde(rename = "application")]
    Application,
    #[serde(rename = "pointer")]
    Pointer,
    #[serde(rename = "comment")]
    Comment,
    #[serde(rename = "reply")]
    Reply,
    #[serde(rename = "article")]
    Article,
    #[serde(rename = "activity")]
    Activity,
    #[serde(rename = "unknown")]
    Unknown,
}
impl_into_bson!(MessageReceiverType);

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum MessageReceiver {
    #[serde(rename = "system")]
    System {
        id: ObjectId,
        club: Option<SimpleClubModel>,
    },
    #[serde(rename = "club")]
    Club(SimpleClubModel),
    #[serde(rename = "application")]
    Application { application_id: ObjectId },
    #[serde(rename = "pointer")]
    Pointer(SimpleUserModel),
    #[serde(rename = "comment")]
    Comment(SimpleClubModel),
    #[serde(rename = "reply")]
    Reply { id: ObjectId, user_id: ObjectId },
    #[serde(rename = "article")]
    Article { id: ObjectId },
    #[serde(rename = "activity")]
    Activity { id: ObjectId },
    #[serde(rename = "unknown")]
    Unknown,
}

impl MessageReceiver {
    pub fn is_reply(&self) -> bool {
        matches!(self, MessageReceiver::Reply { .. })
    }
}
impl Default for MessageReceiver {
    fn default() -> Self {
        Self::Unknown
    }
}
impl_into_bson!(MessageReceiver);

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum MessageReceiverDTO {
    #[serde(rename = "system")]
    System {
        id: String,
        club: Option<SimpleClub>,
    },
    #[serde(rename = "club")]
    Club(SimpleClub),
    #[serde(rename = "application")]
    Application { id: String },
    #[serde(rename = "pointer")]
    Pointer(SimpleUser),
    #[serde(rename = "comment")]
    Comment(SimpleClub),
    #[serde(rename = "reply")]
    Reply { id: String, user_id: String },
    #[serde(rename = "article")]
    Article { id: String },
    #[serde(rename = "activity")]
    Activity { id: String },
    #[serde(rename = "unknown")]
    Unknown,
}

impl From<MessageReceiver> for MessageReceiverDTO {
    fn from(r: MessageReceiver) -> Self {
        match r {
            MessageReceiver::System { id, club } => Self::System {
                id: id.to_string(),
                club: club.map(SimpleClub::from),
            },
            MessageReceiver::Activity { id: activity_id } => Self::Activity {
                id: activity_id.to_string(),
            },
            MessageReceiver::Application { application_id } => Self::Application {
                id: application_id.to_string(),
            },
            MessageReceiver::Article { id: article_id } => Self::Article {
                id: article_id.to_string(),
            },
            MessageReceiver::Club(club) => Self::Club(SimpleClub::from(club)),
            MessageReceiver::Comment(club) => Self::Comment(SimpleClub::from(club)),
            MessageReceiver::Pointer(user) => Self::Pointer(SimpleUser::from(user)),
            MessageReceiver::Reply {
                id: message_id,
                user_id,
            } => Self::Reply {
                id: message_id.to_string(),
                user_id: user_id.to_string(),
            },
            MessageReceiver::Unknown => Self::Unknown,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MessageReaderModel {
    pub id: ObjectId,
    pub nickname: String,
    pub avatar: Option<Assets>,
    pub created_at: DateTime,
}
impl_into_bson!(MessageReaderModel);

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MessageReaderModelDTO {
    pub id: String,
    pub nickname: String,
    pub avatar: Option<Assets>,
    pub created_at: CDateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Model)]
pub struct MessageModel {
    #[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
    pub id: Option<ObjectId>,
    pub sender: Option<SimpleUserModel>,
    pub receiver: MessageReceiver,
    pub title: String,
    pub content: String,
    pub extra: Bson,
    #[serde(default = "Vec::new")]
    pub children: Vec<ObjectId>,
    #[serde(default = "Vec::new")]
    pub readers: Vec<MessageReaderModel>,
    pub valid: bool,
    pub created_at: DateTime,
    pub updated_at: DateTime,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SimpleMessageModel {
    pub id: Option<ObjectId>,
    pub sender: Option<SimpleUserModel>,
    pub receiver: MessageReceiver,
    pub title: String,
    pub content: String,
    pub extra: Bson,
    pub created_at: DateTime,
    pub updated_at: DateTime,
}

impl_into_bson!(SimpleMessageModel);
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SimpleMessage {
    pub id: Option<String>,
    pub sender: Option<SimpleUser>,
    pub receiver: MessageReceiver,
    pub title: String,
    pub content: String,
    pub extra: Value,
    pub created_at: CDateTime<Utc>,
    pub updated_at: CDateTime<Utc>,
}

/// ## MessageHistoryModel
/// 消息修改记录
#[derive(Debug, Clone, Serialize, Deserialize, Model)]
pub struct MessageHistoryModel {
    #[serde(rename = "_id", skip_serializing_if = "Option::is_none")]
    pub id: Option<ObjectId>,
    pub message_id: ObjectId,
    pub before: SimpleMessageModel,
    pub after: SimpleMessageModel,
    pub operator: SimpleUserModel,
    pub created_at: DateTime,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MessageHistoryModelDTO {
    pub id: Option<ObjectId>,
    pub message_id: String,
    pub before: SimpleMessage,
    pub after: SimpleMessage,
    pub operator: SimpleUser,
    pub created_at: CDateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MessageModelDTO {
    pub id: Option<String>,
    pub sender: Option<SimpleUser>,
    pub receiver: MessageReceiverDTO,
    pub title: String,
    pub content: String,
    pub extra: Value,
    pub children: Vec<String>,
    pub readers: Vec<MessageReaderModelDTO>,
    pub created_at: CDateTime<Utc>,
    pub updated_at: CDateTime<Utc>,
}

impl From<MessageReaderModel> for MessageReaderModelDTO {
    fn from(model: MessageReaderModel) -> Self {
        Self {
            id: model.id.to_string(),
            nickname: model.nickname,
            avatar: model.avatar,
            created_at: model.created_at.to_chrono(),
        }
    }
}

impl From<MessageModel> for SimpleMessageModel {
    fn from(model: MessageModel) -> Self {
        Self {
            id: model.id,
            sender: model.sender,
            receiver: model.receiver,
            title: model.title,
            content: model.content,
            extra: model.extra,
            created_at: model.created_at,
            updated_at: model.updated_at,
        }
    }
}

impl From<SimpleMessageModel> for SimpleMessage {
    fn from(model: SimpleMessageModel) -> Self {
        Self {
            id: model.id.map(|oid| oid.to_string()),
            sender: model.sender.map(SimpleUser::from),
            receiver: model.receiver,
            title: model.title,
            content: model.content,
            extra: bson_to_json(&model.extra),
            created_at: model.created_at.to_chrono(),
            updated_at: model.updated_at.to_chrono(),
        }
    }
}

impl From<MessageModel> for SimpleMessage {
    fn from(model: MessageModel) -> Self {
        SimpleMessageModel::from(model).into()
    }
}

impl From<MessageModel> for MessageModelDTO {
    fn from(model: MessageModel) -> Self {
        Self {
            id: model.id.map(|oid| oid.to_string()),
            sender: model.sender.map(SimpleUser::from),
            receiver: MessageReceiverDTO::from(model.receiver),
            title: model.title,
            content: model.content,
            extra: bson_to_json(&model.extra),
            children: model.children.iter().map(ObjectId::to_string).collect(),
            readers: model
                .readers
                .iter()
                .map(|m| MessageReaderModelDTO::from(m.clone()))
                .collect(),
            created_at: model.created_at.to_chrono(),
            updated_at: model.updated_at.to_chrono(),
        }
    }
}

impl Default for MessageModel {
    fn default() -> Self {
        Self {
            id: None,
            sender: None,
            receiver: MessageReceiver::default(),
            title: "".into(),
            content: "".into(),
            extra: Bson::Null,
            children: vec![],
            readers: vec![],
            valid: true,
            created_at: DateTime::now(),
            updated_at: DateTime::now(),
        }
    }
}

impl MessageModel {
    pub fn new<
        S: Into<Option<SimpleUserModel>>,
        R: Into<MessageReceiver>,
        TT: ToString,
        C: ToString,
        E: Into<Option<Bson>>,
    >(
        sender: S,
        receiver: R,
        title: TT,
        content: C,
        extra: E,
    ) -> Self {
        let extra: Bson = extra.into().unwrap_or(Bson::Null);
        Self {
            sender: sender.into(),
            receiver: receiver.into(),
            title: title.to_string(),
            content: content.to_string(),
            extra,
            ..Default::default()
        }
    }
}
